package com.swak.devtools.apidoc.parse.parser;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;

import org.springframework.core.annotation.AnnotatedElementUtils;

import com.swak.annotation.ApiDoc;
import com.swak.devtools.apidoc.parse.ApiClass;
import com.swak.devtools.apidoc.parse.ApiField;
import com.swak.devtools.apidoc.parse.ApiMethod;
import com.swak.entity.IdEntity;
import com.swak.exception.BaseRuntimeException;
import com.swak.persistence.Model;
import com.swak.utils.Methods;
import com.swak.utils.ReflectUtils;

import lombok.Setter;
import lombok.experimental.Accessors;

public class ClassParser extends AbstractParser {

	/**
	 * 解析的基本参数
	 */
	private final Type type;
	private final Class<?> clazz;
	private final Map<TypeVariable<?>, Type> paramVariablesMappers;
	private final ApiClass apiClass;

	/**
	 * 解析过程中是否解析 field、method
	 */
	@Setter
	@Accessors(chain = true)
	private boolean parseField = true;
	@Setter
	@Accessors(chain = true)
	private boolean parseMethod = true;

	private ClassParser(Class<?> clazz) {
		this.type = null;
		this.clazz = clazz;
		this.paramVariablesMappers = null;
		this.apiClass = new ApiClass();
	}

	private ClassParser(ActualClass param) {
		this.type = param.getType();
		this.clazz = param.getClazz();
		this.paramVariablesMappers = param.getParameters();
		this.apiClass = new ApiClass();
	}

	public ApiClass parse() {

		// 基本信息
		this.apiClass.setName(clazz.getName());

		// 描述信息
		ApiDoc doc = AnnotatedElementUtils.findMergedAnnotation(clazz, ApiDoc.class);
		if (doc != null) {
			this.apiClass.setDesc(doc.value());
			this.apiClass.setExample(doc.example());
		}

		// 是数组
		if (isArray(clazz)) {
			this.parseArray();
		}
		// 是集合类型
		else if (isCollection(clazz)) {
			this.parseCollection();
		}

		// 是否需要继续解析
		else if (needParse(clazz)) {

			// 字段和方法
			this.cascadeParse(clazz, paramVariablesMappers);
		}

		return this.apiClass;
	}

	private void parseArray() {
		ApiField apiField = new ApiField();
		apiField.setName("[]");

		// 解析出实际的类型
		Type componentType = ((Class<?>) type).getComponentType();

		// 解析的结果
		ParsedClass parsedClass = parseClass(componentType, clazz, this.paramVariablesMappers);
		apiField.setApiClass(parsedClass.getApiClass());
		this.apiClass.addField(apiField);
	}

	private void parseCollection() {
		ApiField apiField = new ApiField();
		apiField.setName("[]");

		// 解析出实际的类型
		ParameterizedType parameterizedType = (ParameterizedType) type;
		Type actualType = parameterizedType.getActualTypeArguments()[0];

		// 如果是 TypeVariable
		if (actualType instanceof TypeVariable) {
			actualType = this.paramVariablesMappers.get(actualType);
		}

		// 解析的结果
		ParsedClass parsedClass = parseClass(actualType, (Class<?>) parameterizedType.getRawType(),
				this.paramVariablesMappers);

		apiField.setApiClass(parsedClass.getApiClass());
		this.apiClass.addField(apiField);
	}

	private void cascadeParse(Class<?> type, Map<TypeVariable<?>, Type> paramVariablesMappers) {

		// ########## 处理本类 ##############

		this.parseClass(type, paramVariablesMappers);

		// ########## 处理继承 #############

		// 继承的类或接口
		Type[] extendsTypes = null;

		/*
		 * 如果是接口
		 */
		if (type.isInterface()) {
			extendsTypes = type.getGenericInterfaces();
		}

		/*
		 * 如果是类，且排除 Object
		 */
		else if (type.getGenericSuperclass() != null && type.getSuperclass() != Object.class) {
			extendsTypes = new Type[] { type.getGenericSuperclass() };
		}

		// 处理继承的信息
		if (extendsTypes != null) {
			for (Type iType : extendsTypes) {

				// 普通类型
				if (iType instanceof Class) {
					cascadeParse((Class<?>) iType, paramVariablesMappers);
				}

				// 泛型类型
				else if (iType instanceof ParameterizedType
						&& ((ParameterizedType) iType).getRawType() instanceof Class) {
					// 类型映射转换
					ParameterizedType parameterizedType = (ParameterizedType) iType;
					Map<TypeVariable<?>, Type> actualMappers = paramVariablesMappers(
							parameterizedType.getActualTypeArguments(),
							((Class<?>) parameterizedType.getRawType()).getTypeParameters(), paramVariablesMappers);
					// 处理父类型
					cascadeParse((Class<?>) parameterizedType.getRawType(), actualMappers);
				}

				// 其他不支持
				else {
					throw new BaseRuntimeException("Supper class yype not support: " + iType);
				}
			}
		}
	}

	private void parseClass(Class<?> type, Map<TypeVariable<?>, Type> paramVariablesMappers) {

		// parse fields
		if (this.parseField) {
			this.parseFields(type, paramVariablesMappers);
		}

		// parse methods
		if (this.parseMethod) {
			this.parseMethods(type, paramVariablesMappers);
		}
	}

	// ################## 解析字段 ########################
	private void parseFields(Class<?> type, Map<TypeVariable<?>, Type> paramVariablesMappers) {

		// 声明的字段
		Field[] declaredFields = type.getDeclaredFields();

		// 所有的方法，包括父类的
		Method[] methods = type.getMethods();
		for (Method method : methods) {
			String methodName = method.getName();
			if (!methodName.startsWith("set") || Modifier.isStatic(method.getModifiers())) {
				continue;
			}

			// exclude object method
			if (method.getDeclaringClass() == Object.class) {
				continue;
			}

			// support builder set
			Class<?> returnType = method.getReturnType();
			if (!(returnType.equals(Void.TYPE) || returnType.equals(method.getDeclaringClass())
					|| method.getDeclaringClass() == IdEntity.class || method.getDeclaringClass() == Model.class)) {
				continue;
			}

			// only set
			Class<?>[] types = method.getParameterTypes();
			if (types.length != 1) {
				continue;
			}

			char c3 = methodName.charAt(3);
			String propertyName;
			if (Character.isUpperCase(c3) //
					|| c3 > 512 // for unicode method name
			) {
				propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
			} else if (c3 == '_') {
				propertyName = methodName.substring(4);
			} else if (c3 == 'f') {
				propertyName = methodName.substring(3);
			} else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
				propertyName = ReflectUtils.propertyName(methodName.substring(3));
			} else {
				continue;
			}

			// 获得字段，考虑到如果是 boolean字段时的情形
			Field field = ReflectUtils.getField(type, propertyName, declaredFields);
			if (field == null && types[0] == boolean.class) {
				String isFieldName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
				field = ReflectUtils.getField(type, isFieldName, declaredFields);
			}

			// 设置可以访问
			if (field == null) {
				continue;
			}

			method.setAccessible(true);
			field.setAccessible(true);

			// 解析字段
			this.parseField(field, method, paramVariablesMappers);
		}
	}

	private void parseField(Field field, Method method, Map<TypeVariable<?>, Type> paramVariablesMappers) {
		try {
			ParsedStat.start();
			ApiField apiField = FieldParser.parse(field, method, paramVariablesMappers);
			this.apiClass.addField(apiField);
		} finally {
			ParsedStat.finish();
		}
	}

	// ################## 解析方法 ########################

	private void parseMethods(Class<?> type, Map<TypeVariable<?>, Type> paramVariablesMappers) {

		// 当前类声明的类型
		Method[] methods = type.getDeclaredMethods();
		for (Method method : methods) {

			// 不需要解析特殊的方法
			if (!Methods.canProxy(method)) {
				continue;
			}

			// exclude object method
			if (method.getDeclaringClass() == Object.class) {
				continue;
			}

			ApiMethod apiMethod = MethodParser.parse(this.clazz, method, paramVariablesMappers);
			this.apiClass.addMethod(apiMethod);
		}
	}

	public static ClassParser of(ActualClass param) {
		return new ClassParser(param);
	}

	public static ApiClass parse(Class<?> type) {
		return new ClassParser(type).setParseField(false).parse();
	}
}
